
/**
 *	TrueMusic - simple java mp3 player
 *
 *	TrueMusic is free software; you can redistribute it
 *	and/or modify it under the terms of the GNU General
 *	Public License as published by the Free Software
 *	Foundation; either version 2 of the License or (at
 *	your option) any later version.
 *	
 *	This program is distributed in the hope that it will be
 *	useful but WITHOUT ANY WARRANTY; without even the 
 *	implied warranty of the MERCHANTABILITY or FITNESS FOR
 *	A PARTICULAR PURPOSE. See the GNU General Public
 *	License for more details.
 *	
 *	Copyright (c)2005-2006 Ryan Macnak <rmacnak@gmail.com>
 */

package com.servequake.truemusic;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.util.*;
import java.io.*;
import java.awt.dnd.*;
import java.awt.datatransfer.*;

public final class TrueMusic extends JFrame
{
	static TrueMusic inst;

	public static void main(String args[]) throws Exception
	{
	      JFrame.setDefaultLookAndFeelDecorated(true);

		// attempt to match native look & feel
		try{
			UIManager.setLookAndFeel( (LookAndFeel) Class.forName("com.sun.java.swing.plaf.gtk.GTKLookAndFeel").newInstance() );
		}catch(Throwable e){}
		try{
			UIManager.setLookAndFeel( (LookAndFeel) Class.forName("com.sun.java.swing.plaf.mac.MacLookAndFeel").newInstance() );
		}catch(Throwable e){}
		try{
			UIManager.setLookAndFeel( (LookAndFeel) Class.forName("com.sun.java.swing.plaf.windows.WindowsLookAndFeel").newInstance() );
		}catch(Throwable e){}
		try{
			UIManager.put("AuditoryCues.playList",
			UIManager.get("AuditoryCues.allAuditoryCues"));
		}catch(Throwable e){}

		inst = new TrueMusic();
	}

	TrueMusic parent = this;

	ControlPanel cp = new ControlPanel();
	PlaylistPanel pp = new PlaylistPanel();

	Vector<AudioFile> files = new Vector<AudioFile>();
	AudioFile current;
	int sel=0;

	TrueMusic()
	{
		super("TrueMusic 20060618");

		setLayout( new BorderLayout() );

		add( cp, BorderLayout.WEST );
		add( pp );

		pack();
		setSize(600,160);

		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setVisible(true);

		//enable to recieve file drag on all whole frame
		DropTarget dt = new DropTarget(this, pp);

		try
		{
			pp.update();
			cp.update();
		}catch(NullPointerException ee){}

		pp.loadPlaylist("current.m3u");
	}


	void validateCurrent()
	{
		pp.update();

		if( (current!=null) && (! files.contains(current)) ) //if current is no longer on the playlist
		{
			current = null;
		}

		if(current==null&&files.size()>0)
		{
			current = files.get(0);
		}

		cp.update();
	}

	class PlaylistPanel extends JPanel implements ScrollPaneConstants, KeyListener, ActionListener, DropTargetListener, MouseListener, ItemListener
	{
		JList list = new JList();
		JButton add = new JButton("Open");
		JComboBox playlistsbox;

		Vector<String> playlists = new Vector<String>();
		int cursel = 0;

		PlaylistPanel()
		{
			setBorder( BorderFactory.createTitledBorder("") );

			setLayout(new BorderLayout());
			addKeyListener(this);

			playlists.add("Current");
			playlists.add("[New Playlist]");
			playlists.add("[Remove Current Playlist]");
			
			scanPlaylists();

			playlistsbox = new JComboBox(playlists); 
			playlistsbox.setToolTipText("Playlists");
			playlistsbox.addItemListener(this);

			add.addActionListener(this);
			add.setToolTipText("Add mp3 files to your playlist.");

			list.setToolTipText("<html> Current Playlist<br> Press delete to remove the currently selected track.</html>");
 			list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
			list.addKeyListener(this);
			list.setDragEnabled(true); //need to override because default is false
			DropTarget dt = new DropTarget(list, this);
			list.addMouseListener(this);		


			JPanel top = new JPanel(new BorderLayout());
			top.add( playlistsbox );
			top.add( add, BorderLayout.EAST );

			add( top, BorderLayout.NORTH );
			add( new JScrollPane(list, VERTICAL_SCROLLBAR_ALWAYS, HORIZONTAL_SCROLLBAR_NEVER) );
		}

		public void scanPlaylists()
		{
			File dir = new File("./");

			File[] fs = dir.listFiles();

			for(File f:fs)
			{
				String x = f.toString();
				x = x.substring(2);
				if(x.equals("Current.m3u")) continue;

				if( x.toLowerCase().endsWith(".m3u") ) playlists.add(x.substring(0,x.length()-4));
			}
		}

		public void itemStateChanged(final ItemEvent e)
		{
new Thread()
{
public void run()
{
			update();

			if(e.getStateChange()==e.SELECTED)
			{
				int sel = playlistsbox.getSelectedIndex();

				if(sel<0||sel==cursel) return;
				
				if(sel==1)
				{
					String x = JOptionPane.showInputDialog(parent,"Create new playlist");
					if(x==null||x.equals("")) 
					{
						playlistsbox.setSelectedIndex(cursel);
						return;
					}

					playlistsbox.addItem(x);
					playlistsbox.setSelectedIndex(cursel);

					return;
				}
				if(sel==2)
				{
					if(cursel==0)//can't remove "current"
					{
						playlistsbox.setSelectedIndex(0);
						return;
					}


					int op = JOptionPane.showConfirmDialog(parent, "Are you sure you want to remove "+playlists.get(cursel)+"?","Confirm Remove",JOptionPane.YES_NO_OPTION,JOptionPane.QUESTION_MESSAGE);
					
					if(op==JOptionPane.YES_OPTION)
					{
						if(current!=null) current.stop2();

						String del = playlists.get(cursel);
						new File(del+".m3u").delete();
						playlistsbox.removeItemAt(cursel);
				
	
						int nsel = cursel - 1;
						if(nsel==2) nsel = 0;
	
						playlistsbox.setSelectedIndex(nsel);
					}
					else playlistsbox.setSelectedIndex(cursel);

					return;
				}

				if(current!=null)current.stop2();

				String pl = playlists.get(sel);

				cursel=sel;
				loadPlaylist(pl+".m3u");
				validateCurrent();
			}

}
}.start();
		}

		public void loadPlaylist(String fn)
		{
			for(AudioFile taf:files) taf.dispose();
			files.clear();

			try
			{
				File f = new File(fn);
				InputStream is = new FileInputStream(f);

				BufferedReader in = new BufferedReader(new InputStreamReader(is));

				while( true )
				{
					String x = in.readLine();
					if(x==null)break;
		try
		{
					pp.addFile( new File(x) );
		}catch(Exception ex){}
				}
	
				in.close();
				is.close();
			}
			catch(Exception e){}
		}

		public void mouseClicked(MouseEvent e)
		{
			if(e.getClickCount()==2) //double click: play that item
			{
				if(files.size()==0) return; //ignore, no items

				int index = list.locationToIndex(e.getPoint());
				if(index==-1) return;

				int sel = files.indexOf(current);

				if(sel==index) 
				{
					current.play();
					return;
				}

				current.stop2();
				current = files.get(index);
				current.play();

				validateCurrent();		
			}
			else if(e.getButton()==MouseEvent.BUTTON3) //right click: show menu
			{
				if(files.size()==0) return; //ignore, no items

				int index = list.locationToIndex(e.getPoint());
				if(index==-1) return;

				list.setSelectedIndex(index);

				new TrackMenu(files.get(index)).show(list, e.getX(), e.getY() ); 
			}
		}

		class TrackMenu extends JPopupMenu implements ActionListener
		{
			JMenuItem play;
			JMenuItem edit;
			JMenuItem remove;

			AudioFile af;

			TrackMenu(AudioFile af)
			{
				this.af = af;

				play = add("Play");
				edit = add("Edit Tag");
				remove = add("Remove");

				//only mp3file supports tags
				if(!(af instanceof MP3File)) edit.setEnabled(false);

				play.addActionListener(this);
				edit.addActionListener(this);
				remove.addActionListener(this);
			}
			public void actionPerformed(ActionEvent e)
			{
				if(e.getSource()==play)
				{
					if(af==current) af.play();
					else
					{
						current.stop2();
						current = af;
						validateCurrent();
						current.play();
						repaint();
					}
				}
				if(e.getSource()==edit)
				{
					new TagDialog(parent,af);
				}
				if(e.getSource()==remove)
				{
					int pos = list.getSelectedIndex();
	
					AudioFile dead = files.remove(pos);
					pp.update();
					dead.stop2();
					
					savePlaylist();
	
					pos--;
					if(pos<0)pos=0;
	
					if(files.size()!=0)
					{
						list.setSelectedIndex(pos);
					
						if(current==dead)
						{
							current = files.get(pos);
						}
					}
	
					validateCurrent();
					repaint();
	
					dead.dispose();
				}
			}
		}

		public void mouseEntered(MouseEvent e){}
		public void mouseExited(MouseEvent e){}
		public void mousePressed(MouseEvent e){}
		public void mouseReleased(MouseEvent e){}


		public void keyTyped(KeyEvent e){}
	 	public void keyReleased(KeyEvent e){}
		public void keyPressed(KeyEvent e) 
		{
			if(e.getKeyCode()==KeyEvent.VK_DELETE)
			{
				int pos = list.getSelectedIndex();

				AudioFile dead = files.remove(pos);
				pp.update();
				dead.stop2();
				
				savePlaylist();

				pos--;
				if(pos<0)pos=0;

				if(files.size()!=0)
				{
					list.setSelectedIndex(pos);
				
					if(current==dead)
					{
						current = files.get(pos);
					}

				}

				validateCurrent();
				repaint();

				dead.dispose();
			}
		}

		public void actionPerformed(ActionEvent e)
		{
			final JFileChooser chooser = new JFileChooser();
			
			ExampleFileFilter filter = new ExampleFileFilter("mp3","Supported Music Files");
			filter.addExtension("WAV");
			filter.addExtension("AIF");
			//filter.addExtension("RIFF");
			filter.addExtension("AU");
			filter.addExtension("M3U");
			//filter.addExtension("CDA");


			chooser.setDragEnabled(true);
			chooser.setFileFilter(filter);
			chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
			chooser.setMultiSelectionEnabled(true);

			if(chooser.showDialog(parent,"Add to Playlist") == JFileChooser.APPROVE_OPTION)
			{
	new Thread()
	{
	public void run()
	{

				File[] fs = chooser.getSelectedFiles();

				for(File f: fs)
				{
					decide(f);
				}
				savePlaylist();
	}
	}.start();

	    		}
		}

		public void dragEnter(DropTargetDragEvent e){}
		public void dragExit(DropTargetEvent e){}
		public void dragOver(DropTargetDragEvent e){}
		public void dropActionChanged(DropTargetDragEvent e){}
		public void drop( DropTargetDropEvent e)
		{
			e.acceptDrop(1);
			Transferable t = e.getTransferable();

			try
			{
				final java.util.List<File> fl = (java.util.List<File>) t.getTransferData(DataFlavor.javaFileListFlavor);

new Thread()
{
	public void run()
	{
		for(File f: fl)
		{
			decide(f);
		}
		savePlaylist();
	}
}.start();

			}
			catch(Exception ex)
			{}
		}

	
		void decide(File f)
		{
			if(f.isFile()) addFile(f);
			else
			{
				for(File f2: f.listFiles() )
				{
					decide(f2);
				}
			}
		}
	
		void addFile(File f)
		{
			try
			{
				files.add( AudioFile.open(f,cp.prog) );

				validateCurrent();
			}
			catch(Exception ex)
			{
				System.out.print("EX");ex.printStackTrace();
			}
		}
		
		public void savePlaylist()
		{
try
{
			File pf = new File(playlists.get(cursel)+".m3u");
			pf.delete();
			OutputStream os = new FileOutputStream(pf);

			PrintStream out = new PrintStream(os);

			for(AudioFile af: files)
			{
				out.println(af.filename());
			}

			out.close();
}
catch(Exception e){}
		}

		public void update()
		{
			list.setListData(files);
		}
	}

	class ControlPanel extends JPanel implements ActionListener, ChangeListener, SwingConstants
	{
		JLabel title = new JLabel("Add music to your playlist.",CENTER);
		JLabel artist = new JLabel("",CENTER);
		JLabel album = new JLabel("",CENTER);
		JButton play = new JButton(">");
		JButton stop = new JButton("X");
		JButton pause = new JButton("||");
		JButton back = new JButton("<<");
		JButton forw = new JButton(">>");
		JSlider prog = new JSlider(0, 1); 
		JSlider volume = new JSlider(JSlider.VERTICAL,-80,0,-20);
		
		ControlPanel()
		{
			setBorder( BorderFactory.createTitledBorder("") );
			

			setLayout( new BorderLayout() );

			JPanel left = new JPanel( new BorderLayout() );

			play.addActionListener(this);
			stop.addActionListener(this);
			pause.addActionListener(this);
			back.addActionListener(this);
			forw.addActionListener(this);
			prog.addChangeListener(this);
					
			play.setToolTipText("Play");
			stop.setToolTipText("Stop");
			pause.setToolTipText("Pause");
			forw.setToolTipText("Next Track");
			back.setToolTipText("Previous Track");
			prog.setToolTipText("Seek");
			title.setToolTipText("Track Title");
			artist.setToolTipText("Artist");
			album.setToolTipText("Album");

			JPanel row = new JPanel();
			row.add(back);
			row.add(pause);
			row.add(play);
			row.add(stop);
			row.add(forw);
			
			title.setFont( title.getFont().deriveFont(Font.BOLD) );

			JPanel info = new JPanel(new GridLayout(3,1));
			info.add(title);
			info.add(artist);
			info.add(album);

			JPanel bot = new JPanel(new GridLayout(2,1));
			bot.add(row);
			bot.add(prog);

			left.add(info);
			left.add(bot, BorderLayout.SOUTH);

			volume.setToolTipText("Volume");
			add(volume, BorderLayout.EAST);

			add(left);

			pack();
		}

		public void actionPerformed(ActionEvent e)
		{
			if(e.getSource()==play) current.play();
			if(e.getSource()==pause) current.pause();
			if(e.getSource()==stop) current.stop2();
			if(e.getSource()==forw) next();
			if(e.getSource()==back) back();
		}

		public void stateChanged(ChangeEvent e) 
		{
			if(e.getSource()==prog) current.setPos(prog.getValue());
		}

		public void update()
		{
			if(current==null)
			{
				title.setText("Add music to your playlist.");
				artist.setText("");
				album.setText("");
				return;
			}

			title.setText(current.getTitle());
			artist.setText(current.getArtist());
			album.setText(current.getAlbum());
			prog.setMaximum(current.getFrames()); 
		}

		public void next()
		{
			int sel = files.indexOf(current);

			sel++;

			if(files.size()==0)
			{
				current = null;
				title.setText("Add music to your playlist.");
				artist.setText("");
				album.setText("");
				return;
			}

			if(sel>=files.size()) // already at last item
			{
				sel = files.size()-1;
			}
			else
			{
				current.stop2();
				current = files.get(sel);
				current.play();
				pp.list.setSelectedIndex(sel);
				update();
				pp.update();
			}
		}

		public void back()
		{
			int sel = files.indexOf(current);

			sel--;

			if(sel<0) // already at first item
			{
				sel=0;
			}
			else
			{
				current.stop2();
				current = files.get(sel);
				current.play();
				pp.list.setSelectedIndex(sel);
				update();
				pp.update();
			}
		}

	}//control panel

}